package fm.cheyiwang.filter;

import com.alibaba.fastjson.JSON;
import fm.config.MainConfig;
import fm.dto.NeiyiUser;
import fm.dto.UserAddress;
import fm.exception.BizException;
import fm.mongoService.UserService;
import fm.mongoService.TokenService;
import fm.sys.token.ClientUserToken;
import fm.sys.token.Token;
import fm.sys.token.WebUserToken;
import fm.util.CommonUtils;
import fm.util.Constant;
import fm.web.CurrentRequest;
import fm.yichenet.service.AdminUserService;
import me.chanjar.weixin.mp.api.WxMpConfigStorage;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import org.apache.commons.collections.map.HashedMap;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import javax.security.auth.login.FailedLoginException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.Map;

/**
 * 客户端登陆授权过滤器
 * 1.如果有token ，未过期的  则直接登陆
 * 2.如果没有token，但是有手机号码和验证码的，没有openid、unionid的，state=wechatApp的，跳转到微信服务器获取openid、unionid后进行登陆
 * 3.没有token，但是有openid、unionid，能直接关联上用户的直接登陆
 */
public class ClientLoginFilter extends AuthorizationFilter {

    @Autowired
    protected WxMpConfigStorage configStorage;
    @Autowired
    protected WxMpService wxMpService;
    @Autowired
    HttpSession session;

    @Autowired
    UserService userService;

    @Autowired
    MainConfig mainConfig;

    @Autowired
    SessionDAO sessionDAO;

    @Autowired
    AdminUserService adminUserService;
    @Autowired
    TokenService tokenService;


    private static final Logger LOGGER = LoggerFactory.getLogger(ClientLoginFilter.class);

    @Override
    protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {
        if (isLogined()) {
            String redirectUrl = servletRequest.getParameterMap().containsKey("redirectUrl") ? servletRequest.getParameter("redirectUrl") : null;

            if (StringUtils.isNotEmpty(redirectUrl)) {
                LOGGER.info("重定向URL：{}", URLDecoder.decode(redirectUrl, "UTF-8"));
                WebUtils.issueRedirect(servletRequest, servletResponse, getRedirectUrl(URLDecoder.decode(redirectUrl, "UTF-8")));
            }
            return true;
        } else {
            String state = servletRequest.getParameter("state");
            String token = servletRequest.getParameter("token");

            if (StringUtils.isNotEmpty(token)) {
                Token sysToken = tokenService.undecodeToken(token);
                if (!sysToken.isExpired()) {
                    if(state.equalsIgnoreCase("appPhone")){
                        WebUserToken loginToken = new WebUserToken(sysToken.getKey(),sysToken.getOtherData());
                        Subject subject = SecurityUtils.getSubject();
                        subject.login(loginToken);

                    }else{
                        tokenService.tokenLogin(sysToken, state);
                    }
                    return true;
                } else {
                    return false;
                }
            } else {


                if (CommonUtils.isEmpty(state)) {
                    throw new FailedLoginException("未知的设备类型，登陆失败!");
                } else {
                    switch (state) {
                        case MainConfig.weChatApp:
                            return weChatAppLogin(servletRequest, servletResponse);
                        case MainConfig.androidApp:
                        case MainConfig.iosApp:
                        case MainConfig.appPhone:
                            return phoneAppLogin(servletRequest, servletResponse);
                        default:
                            throw new FailedLoginException("未知的设备类型，登陆失败!");
                    }
                }
            }

        }
    }

    /**
     * 获取带有token的重定向URL ，通过这种方式，将token带给微信客户端
     *
     * @param redirectUrl
     * @return
     * @throws Exception
     */
    public String getRedirectUrl(String redirectUrl) throws Exception {
        NeiyiUser user = (NeiyiUser) CurrentRequest.getCurrentUser();
        return getRedirectUrl(redirectUrl, tokenService.buildToken(user.getId(), user.getOpenid()));
    }

    /**
     * 获取重定向参数
     *
     * @param redirectUrl
     * @param token
     * @return
     */
    public String getRedirectUrl(String redirectUrl, String token) {

        String[] redirectUrlArray = redirectUrl.split("\\?");
        redirectUrl = redirectUrlArray[0];
        if (redirectUrlArray.length > 1) {
            LOGGER.info("重定向参数信息:{}", redirectUrlArray[1]);
            String realParams;
            if (redirectUrlArray[1].indexOf("#/") >= 0) {
                String[] paramsArray = redirectUrlArray[1].split("#");
                realParams = "#" + paramsArray[1] + "?" + paramsArray[0];
            } else {
                realParams = "?" + redirectUrlArray[1];
            }
            redirectUrl = redirectUrl + realParams + "&token=" + token;
        } else {
            redirectUrl = redirectUrl + "?token=" + token;
        }
        return redirectUrl;
    }


    /**
     * 判断是否用户已经登录
     *
     * @return
     */
    private boolean isLogined() {
        Subject subject = SecurityUtils.getSubject();
        String id = String.valueOf(subject.getPrincipal());
        if (subject.isAuthenticated()) {
            if ("null".equals(id) || StringUtils.isBlank(id)) {
                sessionDAO.delete(subject.getSession());
                LOGGER.info("user Principal is not exit");
                return false;
            }
            NeiyiUser user = userService.getById(id);
            if (CommonUtils.isEmpty(user)) {
                sessionDAO.delete(subject.getSession());
                LOGGER.info("user data is not exit");
                return false;
            }
            subject.getSession().setAttribute(Constant.SESSION_LOGIN_USER, user);
            LOGGER.info("user authenticated before,re-cache user info  :{}", id);

            return true;
        } else {
            return false;
        }
    }

    /**
     * 微信公众号登陆，用于微信公众号第一次登陆系统，没有手机号码的登陆方式，获取openid，并关联手机
     *
     * @param servletRequest
     * @param servletResponse
     * @return
     * @throws Exception
     */
    private boolean weChatAppLogin(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        String code = servletRequest.getParameter("code");
        String lang = servletRequest.getParameter("lang");
        String state = servletRequest.getParameter("state");
        String phone = servletRequest.getParameter("phone");


        Subject subject = SecurityUtils.getSubject();
        Session session = subject.getSession();

        //未重定向的，需要重定向到微信服务器中
        if (CommonUtils.isEmpty(code)) {
            String redirectUrl = servletRequest.getParameter("redirectUrl");
            if (StringUtils.isNotEmpty(redirectUrl)) {
                session.setAttribute("redirectUrl", redirectUrl);
                LOGGER.info("缓存用户请求目标地址:{}", redirectUrl);
            }
            LOGGER.info("跳转微信授权!");
            WebUtils.issueRedirect(servletRequest, servletResponse, wxMpService.oauth2buildAuthorizationUrl(mainConfig.getDomain() + "/user/login?phone=" + (StringUtils.isEmpty(phone) ? "" : phone), "snsapi_userinfo", MainConfig.weChatApp));
            return false;
        } else {
            WxMpOAuth2AccessToken accessToken = this.wxMpService.oauth2getAccessToken(code);
            String openid = accessToken.getOpenId();
            String unionid = accessToken.getUnionId();


            NeiyiUser user;
            if (StringUtils.isNotEmpty(unionid)) {
                user = userService.getUserByUnionid(unionid);
            } else {
                user = userService.getUserByOpenId(openid);
            }

            if (CommonUtils.isEmpty(user)) {
                user = new NeiyiUser();
                WxMpUser wxMpUser = this.wxMpService.oauth2getUserInfo(accessToken, null);
                LOGGER.info("微信授权获取用户信息返回数据:{}", JSON.toJSONString(wxMpUser));
                user.setOpenid(wxMpUser.getOpenId());
                user.setName(wxMpUser.getNickname());
                user.setUserAdd(new UserAddress(wxMpUser.getCity(), wxMpUser.getProvince(), wxMpUser.getCountry(), null));
                user.setRegType(state);
                user.setUserType(1);
                user.setReviewStatus(0);
                user.setUnionid(wxMpUser.getUnionId());
                user.setRemark(wxMpUser.getRemark());
                user.setUnionid(wxMpUser.getUnionId());
                if (!CommonUtils.isEmpty(phone)) {
                    user.setPhone(phone);
                }
                user.setToken(tokenService.buildToken(user.getId(), user.getOpenid()));
                userService.addUser(user);
            }

            if (StringUtils.isNotEmpty(user.getToken())) {
                LOGGER.warn("首次设定token，重新设定!");
                user.setToken(tokenService.buildToken(user.getId(), user.getOpenid()));
            }

            String token = user.getToken();
            Token sysToken = tokenService.undecodeToken(token);
            if (sysToken.isExpired()) {
                LOGGER.warn("用户的身份token已经过期，重新设定!");
                user.setToken(tokenService.buildToken(user.getId(), user.getOpenid()));
                sysToken = tokenService.undecodeToken(user.getToken());
            }

            session.setAttribute(Constant.SESSION_LOGIN_USER, user);

            LOGGER.info("使用token登陆:{}",JSON.toJSONString(sysToken));
            tokenService.tokenLogin(sysToken, state);

            String redirectUrl = String.valueOf(session.getAttribute("redirectUrl"));
            if (!CommonUtils.isEmpty(redirectUrl)) {
                redirectUrl = getRedirectUrl(redirectUrl, user.getToken());
                LOGGER.info("重定向到微信前端:{}", redirectUrl);
                WebUtils.issueRedirect(servletRequest, servletResponse, redirectUrl);
                return false;
            }
            return true;

        }

    }


    private boolean phoneAppLogin(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        String phone = httpServletRequest.getParameter("phone");
        String openid = httpServletRequest.getParameter("openid");
        String unionid = httpServletRequest.getParameter("unionid");
        String code = httpServletRequest.getParameter("code");
        String state = httpServletRequest.getParameter("state");
        String token = httpServletRequest.getParameter("token");


        if (CommonUtils.isEmpty(token, phone, unionid, openid, code)) {
            throw new BizException("登陆失败，请验证登陆账号与短信验证码是否正确!");
        }


        NeiyiUser user = null;
        if ((StringUtils.isNotEmpty(phone) && StringUtils.isNotEmpty(code))) {
            boolean ret = adminUserService.checkSms(phone, code);
            if (!ret) {
                throw new FailedLoginException("短信验证码错误,登陆失败");

            }
            user = userService.getUserByPhone(phone);
        } else if (StringUtils.isNotEmpty(unionid)) {
            user = userService.getUserByUnionid(unionid);

        } else if (StringUtils.isNotEmpty(openid)) {
            user = userService.getUserByOpenId(openid);
        }


        if (CommonUtils.isEmpty(user)) {
            LOGGER.warn("授权成功，但未找到用户");
            return false;
        }

        //这里对管理员账户做了一个特殊处理,不需要其他短信验证码
        if ("18359219330".equals(phone) && "171028".equals(code)) {
            if (StringUtils.isEmpty(user.getToken()) || tokenService.undecodeToken(user.getToken()).isExpired()) {
                user.setToken(tokenService.buildToken(user.getId(), user.getOpenid()));
            }
            tokenService.tokenLogin(tokenService.undecodeToken(user.getToken()), state);
            return true;
        }


        if (StringUtils.isEmpty(user.getToken())) {
            user.setToken(tokenService.buildToken(user.getId(), user.getOpenid()));
        }

        session.setAttribute(Constant.SESSION_LOGIN_USER, user);
        ClientUserToken clientToken = new ClientUserToken(user.getId(), user.getOpenid(), state);
        Subject subject = SecurityUtils.getSubject();
        subject.login(clientToken);

        return true;

    }


    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
        Map<String, Object> modelMap = new HashedMap();
        modelMap.put("result", -4);
        modelMap.put("msg", "身份验证已经过期，请尝试重新登陆！");
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(JSON.toJSONString(modelMap));
        response.getWriter().flush();
        response.getWriter().close();

        return false;
    }


}
